﻿using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using WelineDynamicIP_LinkSmb;
using static SarjetLib.WelineHandle;

namespace SarjetLib
{
  public partial class WelineHandle
  {
    const int SW_HIDE = 0;
    const int SW_NORMAL = 1;
    const int SW_SHOWMINIMIZED = 2;
    const int SW_MAXIMIZE = 3;
    const int SW_SHOWNOACTIVATE = 4;
    const int SW_SHOW = 5;
    const int SW_MINIMIZE = 6;
    const int SW_SHOWMINNOACTIVE = 7;
    const int SW_SHOWNA = 8;
    const int SW_RESTORE = 9;
    const int SW_SHOWDEFAULT = 10;
    const int SW_FORCEMINIMIZE = 11;

    //当一个窗口或应用程序要关闭时发送一个信号  
    public const int WM_CLOSE = 0x10;

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    static string welinedPath = @"C:\Program Files\Weline\Welined.log";
    static string welinePath = @"C:\ProgramData\Weline\Log";
    static Regex rmatwl = new Regex(@"(?<tm>20\d{2}[-/]\d{2}[-/]\d{2} \d{2}:\d{2}:\d{2}).*?""privip"":""(?<lcalip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"".*?""name"":""HS200"".*?""status"":""online"",.*?""vip"":""(?<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})""", RegexOptions.Compiled);

    public bool WelineIpGet(SambaMapDriverInfo_t samba, string adddir, ref string previp, Action<string> progress = null)
    {
      var fs = new string[0];
      //var fs = Directory.GetFiles(welinePath, "Weline*.log");
      Regex r = new Regex(@"(?<=Weline)\d{8}");
      var dn = DateTime.Now;
      var plist = (from f in fs
                   let tm = Path.GetFileNameWithoutExtension(f)
                   let rtm = r.Match(tm)
                   let dt = DateTime.ParseExact(rtm.Value, "yyyyMMdd", null)
                   let sp = dn - dt
                   where sp.TotalDays < 10// dt.Year == dn.Year && dt.Month == dn.Month && dt.Day == dn.Day
                   orderby dt descending
                   select (f, rtm)
               ).ToList();
      plist.Insert(0, (welinedPath, null));
      if (plist.Count() > 0)
      {
        foreach (var p in plist)
        {
          try
          {
            using (FileStream fsm = new FileStream(p.f, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
              using (StreamReader sr = new StreamReader(fsm, Encoding.UTF8))
              {
                var swl = sr.ReadToEnd();
                var mats = rmatwl.Matches(swl);
                MatchCollection2MatchList matlist = new MatchCollection2MatchList(mats);
                var lastip = (from m in matlist
                              let dt = DateTime.Parse(m.Groups["tm"].Value.Replace("/", "-"))
                              orderby dt descending
                              select (m.Groups["lcalip"].Value, m.Groups["ip"].Value)
                          ).Take(1).ToArray();
                if (lastip.Count() > 0)
                {
                  bool lanok = false;
                  string addr;
                  var ip = lastip.First();
                  var fe = File.Exists("NasLocate=Lan");
                  if (fe)
                  {
                    // 局域网IP
                    string err;
                    if (string.IsNullOrEmpty(previp) || previp != ip.Item1)
                    {
                      previp = ip.Item1;
                      var tryInTime = 3000;
                      err = TryNetUseIp(samba, previp, progress, tryInTime);
                      if (err != "ok")
                      {
                        progress.Invoke($"lan failed");
                      }
                      else
                      {
                        lanok = true;
                      }
                    }
                  }

                  if (!lanok)
                  {
                    fe = File.Exists("NasLocate=VLan");
                    // 虚拟网IP
                    if (fe && (string.IsNullOrEmpty(previp) || previp != ip.Item2))
                    {
                      previp = ip.Item2;
                      var tryInTime = 10000;
                      string err = TryNetUseIp(samba, previp, progress, tryInTime);
                      if (err != "ok")
                      {
                        progress.Invoke($"vlan failed");
                        continue;
                      }
                      SambaMapDriver(samba, previp, progress);
                      progress.Invoke($"{previp}");
                      addr = Path.Combine($@"\\{previp}\", adddir);
                      if (!Directory.Exists(addr))
                      {
                        progress.Invoke($"dir err");
                        continue;
                      }
                      progress.Invoke($@"try open {samba.mapDrive}:\");
                      if (!OpenMnt($@"{samba.mapDrive}:\"))
                      {
                        progress.Invoke($@"try open {addr}");
                        return OpenMnt(addr);
                      }
                      else
                      {
                        return true;
                      }
                    }
                  }

                  progress.Invoke($"{previp}");
                  addr = Path.Combine($@"\\{previp}\", adddir);
                  if (!Directory.Exists(addr))
                  {
                    progress.Invoke($"dir err");
                    continue;
                  }

                  SambaMapDriver(samba, previp, progress);
                  if (!OpenMnt($@"{samba.mapDrive}:\"))
                  {
                    progress.Invoke($@"try open {addr}");
                    return OpenMnt(addr);
                  }
                  else
                  {
                    return true;
                  }
                }
              }
            }
          }
          catch (Exception) { }
        }
      }
      return false;
    }

    private static string TryNetUseIp(SambaMapDriverInfo_t samba, string previp, Action<string> progress, int tryInTime)
    {
      string err;
      progress.Invoke($"try {previp}");
      StartCmd("cmd", $@"net use \\{previp} {samba.password} /user:""{samba.user}""", out err, tryInTime);
      Thread.Sleep(500);
      return err;
    }

    private static void SambaMapDriver(SambaMapDriverInfo_t samba, string previp, Action<string> progress)
    {
      string err;
      progress.Invoke($@"map dir {samba.rootdir}");
      StartCmd("cmd", $@"net use \\{previp}\{samba.rootdir} {samba.password} /user:""{samba.user}""", out err, 3000);
      if (err == "ok")
      {
        progress.Invoke($@"try del {samba.mapDrive}:\");
        StartCmd("cmd", $@"net use {samba.mapDrive}: /del", out err, 3000);
        progress.Invoke($@"map {samba.mapDrive}:\");
        StartCmd("cmd", $@"net use {samba.mapDrive}: \\{previp}\{samba.rootdir}", out err, 3000);
        progress.Invoke($@"try open {samba.mapDrive}:\");
      }
    }

    public bool OpenWelined()
    {
      //ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Service ");
      //var process = Process.GetProcesses();
      //IntPtr MyHwnd = FindWindow(null, "Weline Service");
      //if (MyHwnd == IntPtr.Zero)
      //if (process == null)
      //{
      //StartExe("explorer", $@"/root,""{addr}""", out var err, 0);
      return true;
      //}
      //return false;
    }

    public bool OpenMnt(string addr)
    {
      if (Directory.Exists(addr))
      {
        var mnt = addr.Substring(addr.LastIndexOf('\\') + 1);
        if (!string.IsNullOrEmpty(mnt))
        {
          IntPtr MyHwnd = FindWindow(null, mnt);
          if (MyHwnd == IntPtr.Zero)
          {
            StartExe("explorer", $@"/root,""{addr}""", out var err, 0);
          }
          else
          {
            ShowWindow(MyHwnd, SW_RESTORE);
            SetForegroundWindow(MyHwnd);
          }
        }
        else
        {
          StartExe("explorer", $@"/root,""{addr}""", out var err, 0);
        }
        return true;
      }
      return false;
    }

    public static void StartExe(string exe, string arg, out string err, int timeout = 1000)
    {
      string rerr = "";
      err = "";
      Process we = new Process();
      we.StartInfo.FileName = exe;
      we.StartInfo.Arguments = arg;
      we.StartInfo.CreateNoWindow = true;
      we.StartInfo.UseShellExecute = false;
      we.StartInfo.RedirectStandardInput = true;//是否可以输入
      we.StartInfo.RedirectStandardOutput = true;//是否可以输出
      we.StartInfo.RedirectStandardError = true;//是否可以输出
      we.OutputDataReceived += new DataReceivedEventHandler(delegate (object sender, DataReceivedEventArgs e)
      {
        if (!String.IsNullOrEmpty(e.Data))
        {
          Console.WriteLine(e.Data);
        }
      });
      we.ErrorDataReceived += new DataReceivedEventHandler(delegate (object sender, DataReceivedEventArgs e)
      {
        if (!String.IsNullOrEmpty(e.Data))
        {
          rerr = e.Data;
          Console.WriteLine(e.Data);
        }
      });
      we.Start();
      we.BeginErrorReadLine();
      we.BeginOutputReadLine();
      DateTime tic = DateTime.Now;
      TimeSpan toc;
      do
      {
        toc = DateTime.Now - tic;
      } while (toc.TotalMilliseconds < timeout && rerr == "");
      err = rerr;
    }

    public static void StartCmd(string exe, string arg, out string err, int timeout = 1000)
    {
      string ok = "";
      string rerr = "";
      err = "";
      Process we = new Process();
      we.StartInfo.FileName = "cmd.exe";
      //we.StartInfo.FileName = exe;
      //we.StartInfo.Arguments = arg;
      we.StartInfo.CreateNoWindow = true;
      we.StartInfo.UseShellExecute = false;
      we.StartInfo.RedirectStandardInput = true;//是否可以输入
      we.StartInfo.RedirectStandardOutput = true;//是否可以输出
      we.StartInfo.RedirectStandardError = true;//是否可以输出
      we.OutputDataReceived += new DataReceivedEventHandler(delegate (object sender, DataReceivedEventArgs e)
      {
        if (!String.IsNullOrEmpty(e.Data))
        {
          ok = e.Data;
          Console.WriteLine(e.Data);
        }
      });
      we.ErrorDataReceived += new DataReceivedEventHandler(delegate (object sender, DataReceivedEventArgs e)
      {
        if (!String.IsNullOrEmpty(e.Data))
        {
          rerr = e.Data;
          Console.WriteLine(e.Data);
        }
      });
      DateTime tic = DateTime.Now;
      TimeSpan toc;
      we.Start();
      we.BeginErrorReadLine();
      we.BeginOutputReadLine();

      we.StandardInput.WriteLine("chcp 65001");
      we.StandardInput.Flush();
      do
      {
        toc = DateTime.Now - tic;
        Thread.Sleep(10);
      } while (toc.TotalMilliseconds < 100 && rerr == "");

      we.StandardInput.WriteLine(arg);
      we.StandardInput.Flush();
      ok = "";
      do
      {
        toc = DateTime.Now - tic;
        if (!string.IsNullOrEmpty(ok) && ok.IndexOf("The command completed successfully.") != -1)
        {
          rerr = "ok";
        }
        Thread.Sleep(10);
      } while (toc.TotalMilliseconds < timeout && rerr == "");
      err = rerr;
      we.Close();
    }
  }
}
